package domain.message;

import java.util.HashSet ;
import java.util.Set ;

import junit.framework.Assert ;
import persistence.CorruptDataException ;
import persistence.PersistenceController ;
import persistence.PersistenceFactory ;
import util.Bug ;
import util.NotFoundException ;
import domain.AbstractServerTest ;

/**
 * @author idan
 */
public class MessageServerTest extends AbstractServerTest
{
	private static final int AMOUNT_OF_ELEMENTS_TO_TEST = 3 ;
	private static final int AMOUNT_OF_TIMES_TO_TEST_EXISTENCE = 10 ;
	private static final int AMOUNT_OF_EMTPY_SERVER_TESTS = 100 ;

	private MessageServer server ;
	private MessageServer emptyServer ;

	public MessageServerTest() throws CorruptDataException
	{
		super() ;
	}

    /**
     * Test method for {@link domain.message.MessageServer#MessageServer(persistence.PersistenceFactory)}.
     * Test method for {@link domain.message.MessageServer#close()}.
     */
    public void testConstructorsDestructors() throws CorruptDataException
    {
		final Directory directory = this.server.createDirectory("dir0") ;
		final Thread thread = this.server.createThread("thread0") ;
		final Post post = this.server.createPost("post0", 0, thread.getId()) ;
		this.server.close() ;

		MessageServer serverClone = null ;
		try
		{
			serverClone = new MessageServer(this.persistenceGenerator) ;
			Assert.assertEquals(directory.getName(), serverClone.getDirectory(directory.getId()).getName()) ;
			Assert.assertEquals(thread.getTopic(), serverClone.getThread(thread.getId()).getTopic()) ;
			Assert.assertEquals(post.getWriterId(), serverClone.getPost(post.getId()).getWriterId()) ;
			Assert.assertEquals(post.getMessage(), serverClone.getPost(post.getId()).getMessage()) ;
		}
		catch (final CorruptDataException e)
		{
			Assert.fail("database corruption") ;
		}
		catch (final NotFoundException e)
		{
			Assert.fail("elements were not properly saved") ;
		}
		finally
		{
			if (serverClone != null)
			{
			        serverClone.close() ;
			}
			this.server = new MessageServer(this.persistenceGenerator) ;
		}
    }

	/**
	 * Test method for {@link domain.message.MessageServer#createDirectory(java.lang.String)}.
	 */
	public void testCreateDirectory() throws NotFoundException
	{
		final Directory directory = this.server.createDirectory("dir1") ;
		Assert.assertEquals("dir1", directory.getName()) ;
		Assert.assertSame(directory, this.server.getDirectory(directory.getId())) ;
	}

	/**
	 * Test method for {@link domain.message.MessageServer#createPost(java.lang.String, long)}.
	 */
	public void testCreatePost() throws NotFoundException
	{
		final Thread thread = this.server.createThread("Thread2");
		final Post post = this.server.createPost("post2", 100, thread.getId()) ;
		Assert.assertEquals("post2", post.getMessage()) ;
		Assert.assertEquals(100, post.getWriterId()) ;
		Assert.assertSame(post, this.server.getPost(post.getId())) ;
	}

	/**
	 * Test method for {@link domain.message.MessageServer#createThread(java.lang.String)}.
	 */
	public void testCreateThread() throws NotFoundException
	{
		final Thread thread = this.server.createThread("thread3") ;
		Assert.assertEquals("thread3", thread.getTopic()) ;
		Assert.assertSame(thread, this.server.getThread(thread.getId())) ;
	}

	/**
	 * Test method for {@link domain.message.MessageServer#deleteDirectory(long)}.
	 */
	public void testDeleteDirectory()
	{
		try
		{
			// set some sort of directory structure

			final Directory dir[] = new Directory[MessageServerTest.AMOUNT_OF_ELEMENTS_TO_TEST] ;
			final Thread[] thread = new Thread[dir.length] ;
			final Post post[] = new Post[thread.length] ;

			for (int i = 0 ; i < dir.length ; i++)
			{
				dir[i] = this.server.createDirectory("dir4"+i) ;
				if (i != 0)
				{
					dir[0].addDirectory(dir[i].getId()) ;
				}

				thread[i] = this.server.createThread("thread"+i) ;
				dir[i].addThread(thread[i].getId()) ;

				post[i] = this.server.createPost("post"+i, 100+i ,thread[i].getId()) ;
				thread[i].addPost(post[i].getId()) ;
			}

			// sanity test
			for (int i = 0 ; i < dir.length ; i++)
			{
				Assert.assertTrue(this.server.directoryExists(dir[i].getId())) ;
				Assert.assertTrue(this.server.threadExists(thread[i].getId())) ;
				Assert.assertTrue(this.server.postExists(post[i].getId())) ;
			}

			this.server.deleteDirectory(dir[0].getId()) ;

			// now check that the entire structure is deleted

			for (int i = 0 ; i < dir.length ; i++)
			{
				Assert.assertFalse(this.server.directoryExists(dir[i].getId())) ;
				Assert.assertFalse(this.server.threadExists(thread[i].getId())) ;
				Assert.assertFalse(this.server.postExists(post[i].getId())) ;
			}
		}
		catch (final NotFoundException e)
		{
			Assert.fail("directory not properly created") ;
		}
	}

	/**
	 * Test method for {@link domain.message.MessageServer#deletePost(long)}.
	 */
	public void testDeletePost()
	{
		try
		{
			final Thread thread = this.server.createThread("thread5");
			final Post post = this.server.createPost("post5", 10, thread.getId());
			final long id = post.getId() ;
			Assert.assertTrue(this.server.postExists(id)) ;
			this.server.deletePost(id) ;
			Assert.assertFalse(this.server.postExists(id)) ;
		}
		catch (final NotFoundException e)
		{
			Assert.fail("post not properly created") ;
		}
	}

	/**
	 * Test method for {@link domain.message.MessageServer#deleteThread(long)}.
	 */
	public void testDeleteThread()
	{
		try
		{
			// set a post

			final Thread thread = this.server.createThread("thread5") ;
			final Post post[] = new Post[5] ;

			for (int i = 0 ; i < post.length ; i++)
			{
				post[i] = this.server.createPost("post"+i, 100+i,thread.getId()) ;
				thread.addPost(post[i].getId()) ;
			}

			// sanity test
			Assert.assertTrue(this.server.threadExists(thread.getId())) ;
			for (final Post element: post)
			{
				Assert.assertTrue(this.server.postExists(element.getId())) ;
			}

			this.server.deleteThread(thread.getId()) ;

			// now check that the entire structure is deleted
			for (final Post element: post)
			{
				Assert.assertFalse(this.server.postExists(element.getId())) ;
			}
		}
		catch (final NotFoundException e)
		{
			Assert.fail("directory not properly created") ;
		}
	}

	/**
	 * Test method for {@link domain.message.MessageServer#directoryExists(long)}.
	 */
	public void testDirectoryExists() throws NotFoundException
	{
		for (int i = 1 ; i <= MessageServerTest.AMOUNT_OF_EMTPY_SERVER_TESTS ; i++)
		{
			final long id = this.randomGenerator.nextLong() ;
			Assert.assertFalse(this.emptyServer.directoryExists(id)) ;
		}

		for (int i = 0 ; i < MessageServerTest.AMOUNT_OF_TIMES_TO_TEST_EXISTENCE; i++)
		{
			final Directory dir[] = new Directory[MessageServerTest.AMOUNT_OF_ELEMENTS_TO_TEST] ;
			for (int j = 0 ; j < dir.length ; j++)
			{
				dir[j] = this.server.createDirectory("dir" + (6 + j)) ;
				Assert.assertTrue(this.server.directoryExists(dir[j].getId())) ;
			}
			for (final Directory element: dir)
			{
				this.server.deleteDirectory(element.getId()) ;
				Assert.assertFalse(this.server.directoryExists(element.getId())) ;
			}
		}
	}

	/**
	 * Test method for {@link domain.message.MessageServer#flush()}.
	 * Test method for {@link domain.message.MessageServer#flush(domain.message.Directory)}.
	 * Test method for {@link domain.message.MessageServer#flush(domain.message.Thread)}.
	 * Test method for {@link domain.message.MessageServer#flush(domain.message.Post)}.
	 */
	public void testFlush()
	{
		MessageServer otherServer = null ;
		try
		{

			final Directory directory = this.server.createDirectory("testFlush") ;
			directory.setName("flushed?") ;
			this.server.flush(directory) ;
			otherServer = new MessageServer(this.persistenceGenerator) ;
			Assert.assertEquals(directory.getName(), otherServer.getDirectory(directory.getId()).getName()) ;
			otherServer.close() ;

			final Thread thread = this.server.createThread("testFlush") ;
			thread.setTopic("flushed?") ;
			this.server.flush(thread) ;
			otherServer = new MessageServer(this.persistenceGenerator) ;
			Assert.assertEquals(thread.getTopic(), otherServer.getThread(thread.getId()).getTopic()) ;
			otherServer.close() ;

			final Post post = this.server.createPost("testFlush", 100,thread.getId()) ;
			post.edit("flushed?") ;
			this.server.flush(post) ;
			otherServer = new MessageServer(this.persistenceGenerator) ;
			Assert.assertEquals(post.getMessage(), otherServer.getPost(post.getId()).getMessage()) ;
			otherServer.close() ;

			thread.addPost(post.getId()) ;
			this.server.flush() ;
			otherServer = new MessageServer(this.persistenceGenerator) ;
			Assert.assertTrue(otherServer.getThread(thread.getId()).getPosts().contains(post.getId())) ;
			otherServer.close() ;
		}
		catch (final CorruptDataException e)
		{
			if (otherServer != null)
			{
				otherServer.close() ;
			}
			Assert.fail() ;
		}
		catch (final NotFoundException e)
		{
			if (otherServer != null)
			{
				otherServer.close() ;
			}
			Assert.fail() ;
		}

	}

	/**
	 * Test method for {@link domain.message.MessageServer#getAllDirectories(java.util.Set)}.
	 */
	public void testGetAllDirectoriesById()
	{
		final Set<Directory> created = new HashSet<Directory>() ;
		final Set<Long> ids = new HashSet<Long>() ;
		for (int i = 0 ; i < MessageServerTest.AMOUNT_OF_ELEMENTS_TO_TEST ; i++)
		{
			final Directory temp = this.server.createDirectory("dir" + (11 + i)) ;
			created.add(temp) ;
			ids.add(temp.getId()) ;
		}

		try
		{
			final Set<Directory> obtained = this.server.getAllDirectories(ids) ;
			Assert.assertEquals(created, obtained) ;
		}
		catch (final NotFoundException e)
		{
			Assert.fail() ;
		}
	}

	/**
	 * Test method for {@link domain.message.MessageServer#getAllPosts(java.util.Set)}.
	 */
	public void testGetAllPostsById()
	{
		final Set<Post> created = new HashSet<Post>() ;
		final Set<Long> ids = new HashSet<Long>() ;
		final Thread thread = this.server.createThread("test");
		for (int i = 0 ; i < MessageServerTest.AMOUNT_OF_ELEMENTS_TO_TEST ; i++)
		{
			final Post temp = this.server.createPost("post" + (12 + i), 12+i,thread.getId()) ;
			created.add(temp) ;
			ids.add(temp.getId()) ;
		}

		try
		{
			final Set<Post> obtained = this.server.getAllPosts(ids) ;
			Assert.assertEquals(created, obtained) ;
		}
		catch (final NotFoundException e)
		{
			Assert.fail() ;
		}
	}

	/**
	 * Test method for {@link domain.message.MessageServer#getAllThreads(java.util.Set)}.
	 */
	public void testGetAllThreadsById()
	{
		final Set<Thread> created = new HashSet<Thread>() ;
		final Set<Long> ids = new HashSet<Long>() ;
		for (int i = 0 ; i < MessageServerTest.AMOUNT_OF_ELEMENTS_TO_TEST ; i++)
		{
			final Thread temp = this.server.createThread("thread" + (12 + i)) ;
			created.add(temp) ;
			ids.add(temp.getId()) ;
		}

		try
		{
			final Set<Thread> obtained = this.server.getAllThreads(ids) ;
			Assert.assertEquals(created, obtained) ;
		}
		catch (final NotFoundException e)
		{
			Assert.fail() ;
		}
	}

	/**
	 * Test method for {@link domain.message.MessageServer#getDirectory(long)}.
	 */
	public void testGetDirectory()
	{
		try
		{
			final Directory created = this.server.createDirectory("dir9") ;
			final Directory obtained = this.server.getDirectory(created.getId()) ;
			Assert.assertSame(created, obtained) ;
		}
		catch (final NotFoundException e)
		{
			Assert.fail() ;
		}

		try
		{
			final Directory obtained = this.emptyServer.getDirectory(30) ;
			Assert.fail("fabricated directory: " + obtained) ;
		}
		catch (final NotFoundException e)
		{
			// success
		}
	}

	/**
	 * Test method for {@link domain.message.MessageServer#getPost(long)}.
	 */
	public void testGetPost()
	{
		try
		{
			final Thread thread = this.server.createThread("thread10");
			final Post created = this.server.createPost("post10", 10,thread.getId()) ;
			final Post obtained = this.server.getPost(created.getId()) ;
			Assert.assertSame(created, obtained) ;
		}
		catch (final NotFoundException e)
		{
			Assert.fail() ;
		}

		try
		{
			final Post obtained = this.emptyServer.getPost(30) ;
			Assert.fail("fabricated post: " + obtained) ;
		}
		catch (final NotFoundException e)
		{
			// success
		}
	}

	/**
	 * Test method for {@link domain.message.MessageServer#getThread(long)}.
	 */
	public void testGetThread()
	{
		try
		{
			final Thread created = this.server.createThread("dir9") ;
			final Thread obtained = this.server.getThread(created.getId()) ;
			Assert.assertSame(created, obtained) ;
		}
		catch (final NotFoundException e)
		{
			Assert.fail() ;
		}

		try
		{
			final Thread obtained = this.emptyServer.getThread(30) ;
			Assert.fail("fabricated thread: " + obtained) ;
		}
		catch (final NotFoundException e)
		{
			// success
		}
	}

	/**
	 * Test method for {@link domain.message.MessageServer#postExists(long)}.
	 */
	public void testPostExists() throws NotFoundException
	{
		for (int i = 1 ; i <= MessageServerTest.AMOUNT_OF_EMTPY_SERVER_TESTS ; i++)
		{
			final long id = this.randomGenerator.nextLong() ;
			Assert.assertFalse(this.emptyServer.postExists(id)) ;
		}

		for (int i = 0 ; i < MessageServerTest.AMOUNT_OF_TIMES_TO_TEST_EXISTENCE; i++)
		{
			final Post post[] = new Post[MessageServerTest.AMOUNT_OF_ELEMENTS_TO_TEST] ;
			final Thread thread = this.server.createThread("thread");
			for (int j = 0 ; j < post.length ; j++)
			{
				post[j] = this.server.createPost("post" + (7 + j), 7+j,thread.getId());
				Assert.assertTrue(this.server.postExists(post[j].getId())) ;
			}
			for (final Post element: post)
			{
				this.server.deletePost(element.getId()) ;
				Assert.assertFalse(this.server.postExists(element.getId())) ;
			}
		}
	}

	/**
	 * Test method for {@link domain.message.MessageServer#threadExists(long)}.
	 */
	public void testThreadExists() throws NotFoundException
	{
		for (int i = 1 ; i <= MessageServerTest.AMOUNT_OF_EMTPY_SERVER_TESTS ; i++)
		{
			final long id = this.randomGenerator.nextLong() ;
			Assert.assertFalse(this.emptyServer.threadExists(id)) ;
		}

		for (int i = 0 ; i < MessageServerTest.AMOUNT_OF_TIMES_TO_TEST_EXISTENCE; i++)
		{
			final Thread thread[] = new Thread[MessageServerTest.AMOUNT_OF_ELEMENTS_TO_TEST] ;
			for (int j = 0 ; j < thread.length ; j++)
			{
				thread[j] = this.server.createThread("thread" + (8 + j)) ;
				Assert.assertTrue(this.server.threadExists(thread[j].getId())) ;
			}
			for (final Thread element: thread)
			{
				this.server.deleteThread(element.getId()) ;
				Assert.assertFalse(this.server.threadExists(element.getId())) ;
			}
		}
	}

	@Override
	protected void clearDatabase()
	{
		try
		{
			PersistenceFactory freshPersistenceBuilder = this
				.newPersistenceGenerator(PersistenceController.BUILD_NEW, false) ;
			freshPersistenceBuilder.newDirecoryPersistence().close() ;
			freshPersistenceBuilder.newPostPersistence().close() ;
			freshPersistenceBuilder.newThreadPersistence().close() ;
		}
		catch (CorruptDataException e)
		{
			throw new Bug("report corrupt data when no data expected") ;
		}
	}

	@Override
	protected void setUp() throws Exception
	{
		super.setUp() ;
		this.server = new MessageServer(this.persistenceGenerator) ;
		this.emptyServer = new MessageServer(this.newPersistenceGenerator(PersistenceController.BUILD_NEW, true)) ;
	}
}
